#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>



#define IMX6UIRQ_CNT		1			/* 设备号个数 	*/
#define IMX6UIRQ_NAME		"blockio"	/* 名字 		*/
#define KEY0VALUE			0X01		/* KEY0按键值 	*/
#define INVAKEY				0XFF		/* 无效的按键值 */
#define KEY_NUM				1			/* 按键数量 	*/

/* 中断IO描述结构体 */
struct irq_keydesc
{
    int gpio;						/* gpio */
    int irqnum;                        /* 中断号 */
    unsigned char value;				/* 按键值 */
    char name[10];						/* 名字 */
    irqreturn_t (*handler)(int, void *);	/* 中断处理函数 */
    struct tasklet_struct tasklet;        /* 任务let */
};

struct imx6uirq_dev
{
    dev_t devid;				/* 设备号 		*/
    struct cdev cdev;			/* cdev 		*/
    struct class *class;		/* 类 		*/
    struct device *device;		/* 设备 		*/
    int major;					/* 主设备号 	*/
    int minor;					/* 次设备号 	*/
    struct device_node *nd;		/* 设备节点 	*/
    atomic_t releasekey;		/* 释放标志 	*/
    atomic_t keyvalue;			/* 按键值 		*/
    struct timer_list timer;/* 定义一个定时器*/
    struct irq_keydesc irqkeydesc[KEY_NUM];	/* 按键描述数组 */
    unsigned char curkeynum;	/* 当前按键号 	*/

    wait_queue_head_t r_wait;	/* 阻塞队列头 */
};
struct imx6uirq_dev imx6uirq;	/* irq设备 */

static irqreturn_t key0_handler(int irq, void *dev_id)
{
    struct imx6uirq_dev *dev = (struct imx6uirq_dev *)dev_id;
    dev->curkeynum = 0;		/* KEY0 */
    dev->timer.data = (volatile long)dev_id;
    mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10));	/* 10ms */
    return IRQ_RETVAL(IRQ_HANDLED);
}
static void timer_function(unsigned long arg)
{
    unsigned char value;
    unsigned char num;
    struct irq_keydesc *keydesc;
    struct imx6uirq_dev *dev = (struct imx6uirq_dev *)arg;

    num = dev->curkeynum;
    keydesc = &dev->irqkeydesc[num];

    value = gpio_get_value(dev->irqkeydesc[dev->curkeynum].gpio);
    if (value == 0)	/* 按键按下 */
    {
        atomic_set(&dev->keyvalue, dev->irqkeydesc[dev->curkeynum].value);
    }
    else	/* 按键释放 */
    {
        atomic_set(&dev->keyvalue, 0x80 | keydesc->value);
        atomic_set(&dev->releasekey, 1);
    }
    //唤醒进程
    if (atomic_read(&dev->releasekey))
    {
        wake_up_interruptible(&dev->r_wait);
    }
}
static int keyio_init(void){
    unsigned char i = 0;
    int ret = 0;

    imx6uirq.nd = of_find_node_by_path("/key");
    if (imx6uirq.nd == NULL)
    {
        printk("key node not find!\r\n");
        return -EINVAL;
    }
    for ( i = 0; i < KEY_NUM; i++)
    {
        imx6uirq.irqkeydesc[i].gpio = of_get_named_gpio(imx6uirq.nd, "key-gpios", i);
        if (imx6uirq.irqkeydesc[i].gpio < 0)
        {
            printk("can't get key%d\r\n", i);
        }
        memset(imx6uirq.irqkeydesc[i].name, 0, sizeof(imx6uirq.irqkeydesc[i].name));
        sprintf(imx6uirq.irqkeydesc[i].name, "KEY%d", i);
        gpio_request(imx6uirq.irqkeydesc[i].gpio, imx6uirq.irqkeydesc[i].name);
        gpio_direction_input(imx6uirq.irqkeydesc[i].gpio);
        imx6uirq.irqkeydesc[i].irqnum = irq_of_parse_and_map(imx6uirq.nd, i);
    }
    imx6uirq.irqkeydesc[0].handler = key0_handler;
    imx6uirq.irqkeydesc[0].value = KEY0VALUE;
    for ( i = 0; i < KEY_NUM; i++)
    {
        ret = request_irq(imx6uirq.irqkeydesc[i].irqnum, imx6uirq.irqkeydesc[i].handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, imx6uirq.irqkeydesc[i].name, (void *)&imx6uirq);;
        if (ret < 0)
        {
            printk("irq %d request failed!\r\n", imx6uirq.irqkeydesc[i].irqnum);
            return -EFAULT;
        }
    }
    init_timer(&imx6uirq.timer);
    imx6uirq.timer.function = timer_function;
    //初始化等待队列头
    init_waitqueue_head(&imx6uirq.r_wait);
    return 0;
}

static int imx6uirq_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &imx6uirq;	/* 设置私有数据 */
    return 0;
}
static ssize_t imx6uirq_read(struct file *filp,char __user *buf,size_t cnt,loff_t *offt)
{
    int ret = 0;
    unsigned char keyvalue = 0;
    unsigned char releasekey = 0;
    struct imx6uirq_dev *dev = (struct imx6uirq_dev *)filp->private_data;
#if 0
    //等待事件
    wait_event_interruptible(dev->r_wait, atomic_read(&dev->keyvalue));
#endif

    //定义一个等待队列项
    DECLARE_WAITQUEUE(wait, current);	/* 定义一个等待队列 */

    add_wait_queue(&dev->r_wait, &wait);	/* 添加到等待队列 */
    __set_current_state(TASK_INTERRUPTIBLE);	/* 设置为可中断等待 */
    schedule();	/* 进程阻塞 切换 */
    //唤醒之后从这里运行
    if (signal_pending(current))	{			/* 判断是否为信号引起的唤醒 */
        ret = -ERESTARTSYS;
        goto data_error;
    }


#if 0
    if (atomic_read(&dev->keyvalue) == 0)	/* 没有按键按下 */
    {
        //如果是非阻塞
        if (filp->f_flags & O_NONBLOCK)
        {
            return -EAGAIN;
        }
        //如果是阻塞
        //add_wait_queue(&dev->r_wait, &wait);	/* 添加到等待队列 */
       // schedule();	/* 进程阻塞 */
        // if(signal_pending(current))	{			/* 判断是否为信号引起的唤醒 */
		// 	ret = -ERESTARTSYS;
		// 	goto wait_error;
		// }
        // __set_current_state(TASK_RUNNING);	/* 设置为运行态 */
        // remove_wait_queue(&dev->r_wait, &wait);	/* 移除等待队列 */
    }
#endif
    keyvalue = atomic_read(&dev->keyvalue);
    releasekey = atomic_read(&dev->releasekey);
    if (releasekey == 1)
    {
        if(keyvalue & 0x80)
        {
            keyvalue &= 0x7F;
            ret = copy_to_user(buf, &keyvalue, sizeof(keyvalue));
        }else
        {
            goto data_error;
        }
        atomic_set(&dev->releasekey, 0);/* 按下标志清零 */
    }else{
        goto data_error;
    }


data_error:
	set_current_state(TASK_RUNNING);		/* 设置任务为运行态 */
    printk("dataerror\r\n");
	remove_wait_queue(&dev->r_wait, &wait);	/* 将等待队列移除 */

    return ret;
}
static int imx6uirq_release(struct inode *inode, struct file *filp)
{
    filp->private_data = NULL;
    return 0;
}
static struct file_operations imx6uirq_fops = {
    .owner = THIS_MODULE,
    .open = imx6uirq_open,
    .read = imx6uirq_read,
    .release = imx6uirq_release,
};

static int __init imx6uirq_init(void)
{
    int ret = 0;
    //注册字符设备驱动
    if (imx6uirq.major)
    {
        imx6uirq.devid = MKDEV(imx6uirq.major, 0);
        register_chrdev_region(imx6uirq.devid, IMX6UIRQ_CNT, IMX6UIRQ_NAME);
    }
    else
    {
        alloc_chrdev_region(&imx6uirq.devid, 0, IMX6UIRQ_CNT, IMX6UIRQ_NAME);
        imx6uirq.major = MAJOR(imx6uirq.devid);
        imx6uirq.minor = MINOR(imx6uirq.devid);
    }
    printk("imx6uirq major = %d, minor = %d\r\n", imx6uirq.major, imx6uirq.minor);
    //初始化字符设备
    imx6uirq.cdev.owner = THIS_MODULE;
    cdev_init(&imx6uirq.cdev, &imx6uirq_fops);
    cdev_add(&imx6uirq.cdev, imx6uirq.devid, IMX6UIRQ_CNT);
    //创建类
    imx6uirq.class = class_create(THIS_MODULE, IMX6UIRQ_NAME);
    if (IS_ERR(imx6uirq.class))
    {
        return PTR_ERR(imx6uirq.class);
    }
    //创建设备
    imx6uirq.device = device_create(imx6uirq.class, NULL, imx6uirq.devid, NULL, IMX6UIRQ_NAME);
    if (IS_ERR(imx6uirq.device))
    {
        return PTR_ERR(imx6uirq.device);
    }
    atomic_set(&imx6uirq.keyvalue, INVAKEY);	/* 初始化按键值 */
    atomic_set(&imx6uirq.releasekey, 0);		/* 初始化释放标志 */
    init_waitqueue_head(&imx6uirq.r_wait);	/* 初始化等待队列头 */
    ret = keyio_init();
    if (ret < 0)
    {
        return ret;
    }
    return 0;

}
static void __exit imx6uirq_exit(void)
{
    unsigned char i = 0;
    for ( i = 0; i < KEY_NUM; i++)
    {
        free_irq(imx6uirq.irqkeydesc[i].irqnum, (void *)&imx6uirq);
        gpio_free(imx6uirq.irqkeydesc[i].gpio);
    }
    cdev_del(&imx6uirq.cdev);
    unregister_chrdev_region(imx6uirq.devid, IMX6UIRQ_CNT);
    device_destroy(imx6uirq.class, imx6uirq.devid);
    class_destroy(imx6uirq.class);
}

module_init(imx6uirq_init);
module_exit(imx6uirq_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("wxy");